/* * Copyright (C) 2011 Toshiaki Maki <makingx@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package am.ik.aws.apa; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.ws.BindingProvider; import javax.xml.ws.Response; import javax.xml.ws.WebServiceException; import am.ik.aws.apa.handler.AwsHandlerResolver; import am.ik.aws.apa.jaxws.AWSECommerceService; import am.ik.aws.apa.jaxws.AWSECommerceServicePortType; import am.ik.aws.apa.jaxws.ItemLookup; import am.ik.aws.apa.jaxws.ItemLookupRequest; import am.ik.aws.apa.jaxws.ItemLookupResponse; import am.ik.aws.apa.jaxws.ItemSearch; import am.ik.aws.apa.jaxws.ItemSearchRequest; import am.ik.aws.apa.jaxws.ItemSearchResponse; import am.ik.aws.config.AwsConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AwsApaRequesterImpl implements AwsApaRequester { private final String endpoint; private final String accessKeyId; private final String secretAccessKey; private final String associateTag; private final Lock lock = new ReentrantLock(); private volatile AWSECommerceServicePortType port; private static final Logger logger = LoggerFactory .getLogger(AwsApaRequesterImpl.class); private int retryCount = 3; private long retryInterval = 1000; // [msec] private static final Pattern HTTP_STATUS_PATTERN = Pattern .compile("status code ([0-9]{3})"); public AwsApaRequesterImpl() throws IllegalArgumentException { this.endpoint = AwsConfig.getValue("aws.endpoint"); this.accessKeyId = AwsConfig.getValue("aws.accesskey.id"); this.secretAccessKey = AwsConfig.getValue("aws.secret.accesskey"); this.associateTag = AwsConfig.getValue("aws.associate.tag"); checkArgs(endpoint, accessKeyId, secretAccessKey, associateTag); } public AwsApaRequesterImpl(String endpoint, String accessKeyId, String secretAccessKey, String associateTag) throws IllegalArgumentException { this.endpoint = endpoint; this.accessKeyId = accessKeyId; this.secretAccessKey = secretAccessKey; this.associateTag = associateTag; checkArgs(endpoint, accessKeyId, secretAccessKey, associateTag); } private static void checkArgs(String endpoint, String accessKeyId, String secretAccessKey, String associateTag) throws IllegalArgumentException { checkIfNullOrEmpty(endpoint, "endpoint"); checkIfNullOrEmpty(accessKeyId, "accessKeyId"); checkIfNullOrEmpty(secretAccessKey, "secretAccessKey"); checkIfNullOrEmpty(associateTag, "associateTag"); } private static void checkIfNullOrEmpty(String str, String name) throws IllegalArgumentException { if (str == null) { throw new IllegalArgumentException(name + " is null."); } if ("".equals(str)) { throw new IllegalArgumentException(name + " is empty."); } } protected AWSECommerceServicePortType preparePort() { if (port == null) { try { lock.lock(); if (port == null) { logger.debug("preparing..."); AWSECommerceService service = new AWSECommerceService(); service.setHandlerResolver(new AwsHandlerResolver( secretAccessKey)); AWSECommerceServicePortType port = service .getAWSECommerceServicePort(); ((BindingProvider) port) .getRequestContext() .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint + "/onca/soap?Service=AWSECommerceService"); this.port = port; } } finally { lock.unlock(); } } return port; } protected ItemSearch prepareItemSearch(ItemSearchRequest request) { ItemSearch itemSearch = new ItemSearch(); itemSearch.setAssociateTag(associateTag); itemSearch.setAWSAccessKeyId(accessKeyId); itemSearch.getRequest().add(request); return itemSearch; } protected ItemLookup prepareItemLookup(ItemLookupRequest request) { ItemLookup itemLookup = new ItemLookup(); itemLookup.setAssociateTag(associateTag); itemLookup.setAWSAccessKeyId(accessKeyId); itemLookup.getRequest().add(request); return itemLookup; } @Override public ItemSearchResponse itemSearch(ItemSearchRequest request) { final AWSECommerceServicePortType port = preparePort(); final ItemSearch itemSearch = prepareItemSearch(request); ItemSearchResponse response = invokeWithRetry(new WebServiceInvoker<ItemSearchResponse>() { @Override public ItemSearchResponse invoke() throws WebServiceException { return port.itemSearch(itemSearch); } }); return response; } @Override public Response<ItemSearchResponse> itemSearchAsync( ItemSearchRequest request) throws ExecutionException, InterruptedException { AWSECommerceServicePortType port = preparePort(); ItemSearch itemSearch = prepareItemSearch(request); Response<ItemSearchResponse> response = port .itemSearchAsync(itemSearch); return response; } public <T> T invokeWithRetry(WebServiceInvoker<T> invoker) throws WebServiceException { int retry = 0; T result = null; while (true) { try { result = invoker.invoke(); break; } catch (WebServiceException e) { Matcher m = HTTP_STATUS_PATTERN.matcher(e.getMessage()); if (m.find() && Integer.parseInt(m.group(1)) == 503) { logger.warn("web service exception occurred", e); if (retry < retryCount && retryInterval > 0) { retry++; try { logger.debug("retry {}/{}", retry, retryCount); TimeUnit.MILLISECONDS.sleep(retryInterval * retry); } catch (InterruptedException ignored) { } continue; } else { throw e; } } } } return result; } @Override public ItemLookupResponse itemLookup(ItemLookupRequest request) { final AWSECommerceServicePortType port = preparePort(); final ItemLookup itemLookup = prepareItemLookup(request); ItemLookupResponse response = invokeWithRetry(new WebServiceInvoker<ItemLookupResponse>() { @Override public ItemLookupResponse invoke() throws WebServiceException { return port.itemLookup(itemLookup); } }); return response; } @Override public Response<ItemLookupResponse> itemLookupAsync( ItemLookupRequest request) throws ExecutionException, InterruptedException { AWSECommerceServicePortType port = preparePort(); ItemLookup itemLookup = prepareItemLookup(request); Response<ItemLookupResponse> response = port .itemLookupAsync(itemLookup); return response; } public <T> T getResponseWithRetry(final Response<T> res) { return invokeWithRetry(new WebServiceInvoker<T>() { @Override public T invoke() throws WebServiceException { try { return res.get(); } catch (InterruptedException e) { throw new WebServiceException(e); } catch (ExecutionException e) { throw new WebServiceException(e); } } }); } public int getRetryCount() { return retryCount; } public void setRetryCount(int retryCount) { this.retryCount = retryCount; } public long getRetryInterval() { return retryInterval; } public void setRetryInterval(long retryInterval) { this.retryInterval = retryInterval; } }